#include "StdAfx.h"
#include "TwXfile.h"
#include "TwEngineDx.h"


int MaxFrames = 20;
TwXfile::TwXfile(void)
{
}

TwXfile::~TwXfile(void)
{
}

void TwXfile::link_frames(CUSTOM_FRAME* pFrame)
{
	// cast the pFrame's mesh container pointer to a CUSTOM_MESHCONTAINER*
	CUSTOM_MESHCONTAINER* pMeshContainer = (CUSTOM_MESHCONTAINER*)pFrame->pMeshContainer;

	// if there is a mesh container and if it has skin info...
	if(pMeshContainer && pMeshContainer->pSkinInfo)
	{
		// loop through each frame in the mesh container
		for(UINT i = 0; i < pMeshContainer->pSkinInfo->GetNumBones(); i++)
		{
			CUSTOM_FRAME* pTempFrame;    // a temporary frame pointer

			// find each frame by name
			pTempFrame = (CUSTOM_FRAME*)D3DXFrameFind(TopFrame,
				pMeshContainer->pSkinInfo->GetBoneName(i));
			// and set up a pointer to it
			pMeshContainer->ppFrameMatrices[i] = &pTempFrame->CombTransformationMatrix;
		}
	}

	// run for all siblings
	if(pFrame->pFrameSibling)
		link_frames((CUSTOM_FRAME*)pFrame->pFrameSibling);

	// run for the first child (which will then run all other children)
	if(pFrame->pFrameFirstChild)
		link_frames((CUSTOM_FRAME*)pFrame->pFrameFirstChild);
}


void TwXfile::update_frames(CUSTOM_FRAME* pFrame, D3DXMATRIX* pParentMatrix)
{
	// combine the frame's matrix with the parent's matrix, if any
	if(pParentMatrix)
		pFrame->CombTransformationMatrix = pFrame->TransformationMatrix * *pParentMatrix;
	else
		pFrame->CombTransformationMatrix = pFrame->TransformationMatrix;

	// run for all siblings
	if(pFrame->pFrameSibling)
		update_frames((CUSTOM_FRAME*)pFrame->pFrameSibling, pParentMatrix);

	// run for the first child (which will then run all other children)
	if(pFrame->pFrameFirstChild)
		update_frames((CUSTOM_FRAME*)pFrame->pFrameFirstChild,
		&pFrame->CombTransformationMatrix);
}


void TwXfile::update_mesh_containers(CUSTOM_FRAME* pFrame)
{
	// cast the pFrame's mesh container pointer to a CUSTOM_MESHCONTAINER*
	CUSTOM_MESHCONTAINER* pMeshContainer = (CUSTOM_MESHCONTAINER*)pFrame->pMeshContainer;

	if(pMeshContainer && pMeshContainer->pSkinInfo)
	{
		UINT NumFrames = pMeshContainer->pSkinInfo->GetNumBones();    // find how many frames
		// for each frame in the mesh container...
		for(UINT i = 0; i < NumFrames; i++)
		{
			// set FinalMatrices to that frame's offset matrix
			FinalMatrices[i] = *pMeshContainer->pSkinInfo->GetBoneOffsetMatrix(i);

			// multiply that by the animated frame matrix
			FinalMatrices[i] *= *pMeshContainer->ppFrameMatrices[i];
		}

		void* pSrc = NULL;    // a void pointer for the original mesh
		void* pDst = NULL;    // a void pointer for the modified mesh

		// lock the two meshes
		pMeshContainer->MeshData.pMesh->LockVertexBuffer(NULL, &pSrc);
		pMeshContainer->pFinalMesh->LockVertexBuffer(NULL, &pDst);

		// store the animated mesh into FinalMesh
		pMeshContainer->pSkinInfo->UpdateSkinnedMesh(FinalMatrices, NULL, pSrc, pDst);

		// unlock the two meshes
		pMeshContainer->pFinalMesh->UnlockVertexBuffer();
		pMeshContainer->MeshData.pMesh->UnlockVertexBuffer();
	}

	// run for all siblings
	if(pFrame->pFrameSibling)
		update_mesh_containers((CUSTOM_FRAME*)pFrame->pFrameSibling);

	// run for the first child (which will then run all other children)
	if(pFrame->pFrameFirstChild)
		update_mesh_containers((CUSTOM_FRAME*)pFrame->pFrameFirstChild);
}


void TwXfile::draw_mesh(CUSTOM_FRAME* pFrame)
{
	// cast the pFrame's mesh container pointer to a CUSTOM_MESHCONTAINER*
	CUSTOM_MESHCONTAINER* pMeshContainer = (CUSTOM_MESHCONTAINER*)pFrame->pMeshContainer;

	if(pMeshContainer)
	{
		// for each material...
		for(UINT i = 0; i < pMeshContainer->NumMaterials; i++)
		{
			// set the material
			g_Render->GetDevice()->SetMaterial(&pMeshContainer->pMaterials[i].MatD3D);
			// set the texture
			g_Render->GetDevice()->SetTexture(0, pMeshContainer->pTextures[i]);

			// draw the subset!
			pMeshContainer->pFinalMesh->DrawSubset(i);
		}
	}

	// run for all siblings
	if(pFrame->pFrameSibling)
		draw_mesh((CUSTOM_FRAME*)pFrame->pFrameSibling);

	// run for the first child (which will then run all other children)
	if(pFrame->pFrameFirstChild)
		draw_mesh((CUSTOM_FRAME*)pFrame->pFrameFirstChild);
}
// this is the function used to render a single frame
void TwXfile::render_frame(void)
{
	g_Render->GetDevice()->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
	g_Render->GetDevice()->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

	g_Render->GetDevice()->BeginScene();

	// SET UP THE TRANSFORMS

	D3DXMATRIX matView;    // the view transform matrix
	D3DXMatrixLookAtLH(&matView,
		&D3DXVECTOR3 (1000.0f, 100.0f, 0.0f),    // the camera position
		&D3DXVECTOR3 (0.0f, 0.0f, 0.0f),    // the look-at position
		&D3DXVECTOR3 (0.0f, 1.0f, 0.0f));    // the up direction
	g_Render->GetDevice()->SetTransform(D3DTS_VIEW, &matView);    // set the view transform to matView 

	D3DXMATRIX matProjection;    // the projection transform matrix
	D3DXMatrixPerspectiveFovLH(&matProjection,
		D3DXToRadian(45),    // the horizontal field of view
		(FLOAT)800 / (FLOAT)600,    // aspect ratio
		1.0f,    // the near view-plane
		2000.0f);    // the far view-plane
	g_Render->GetDevice()->SetTransform(D3DTS_PROJECTION, &matProjection);    // set the projection

	static float index = 0.0f; index+=0.03f;    // an ever-increasing float value
	D3DXMATRIX matRotateY;    // a matrix to store the rotation for each triangle
	D3DXMatrixRotationY(&matRotateY, index);    // the rotation matrix
	g_Render->GetDevice()->SetTransform(D3DTS_WORLD, &matRotateY);    // set the world transform

	// if the mesh is animated...
	if(AnimationController)
	{
		static DWORD Time = GetTickCount();

		// move the animation forward by the elapsed time
		AnimationController->AdvanceTime((GetTickCount() - Time) * 0.001f, NULL);

		// reset Time for the next time through
		Time = GetTickCount();
	}

	// update each combined matrix
	update_frames((CUSTOM_FRAME*)TopFrame, NULL);

	// update the mesh containers
	update_mesh_containers((CUSTOM_FRAME*)TopFrame);

	// render each mesh container
	draw_mesh((CUSTOM_FRAME*)TopFrame);

	g_Render->GetDevice()->EndScene(); 

	g_Render->GetDevice()->Present(NULL, NULL, NULL, NULL);

	return;
}
// this is the function that puts the 3D models into video RAM
void TwXfile::init_graphics(void)
{
	MeshAllocation MemAllocator;    // create a mesh allocation class

	// load the mesh and call the allocation functions
	D3DXLoadMeshHierarchyFromX((LPCSTR)"tiny.x",
		D3DXMESH_MANAGED,
		g_Render->GetDevice(),
		&MemAllocator,
		NULL,
		&TopFrame,
		&AnimationController);

	// allocate TwMemory for the FinalMatrices array
	FinalMatrices = new D3DXMATRIX[MaxFrames];
	ZeroMemory(FinalMatrices, sizeof(D3DXMATRIX) * MaxFrames);

	// set up the ppFrameMatrices pointers
	link_frames((CUSTOM_FRAME*)TopFrame);
}


// this is the function that sets up the lights and materials
void TwXfile::init_light(void)
{
	D3DLIGHT9 light;    // create the light struct

	ZeroMemory(&light, sizeof(light));    // clear out the struct for use
	light.Type = D3DLIGHT_DIRECTIONAL;    // make the light type 'directional light'
	light.Diffuse.r = 0.5f;    // .5 red
	light.Diffuse.g = 0.5f;    // .5 green
	light.Diffuse.b = 0.5f;    // .5 blue
	light.Diffuse.a = 1.0f;    // full alpha (we'll get to that soon)

	D3DVECTOR vecDirection = {-1.0f, -0.3f, -1.0f};    // the direction of the light
	light.Direction = vecDirection;    // set the direction

	g_Render->GetDevice()->SetLight(0, &light);    // send the light struct properties to light #0
	g_Render->GetDevice()->LightEnable(0, TRUE);    // turn on light #0

	return;
}



